package cn.jtfadmin.core.modules.base.region.service.impl;

import cn.jtfadmin.base.data.jpa.service.JpaTreeService;
import cn.jtfadmin.base.data.jpa.utils.TreeUtils;
import cn.jtfadmin.base.dict.dto.DictItemModelDTO;
import cn.jtfadmin.base.dict.dto.DictModelDTO;
import cn.jtfadmin.base.dict.service.DictEntityService;
import cn.jtfadmin.base.dict.service.DynamicDictConfig;
import cn.jtfadmin.base.lang.common.domain.dto.*;
import cn.jtfadmin.base.lang.common.utils.JsonUtils;
import cn.jtfadmin.core.modules.base.region.domain.dto.AreaDTO;
import cn.jtfadmin.core.modules.base.region.domain.dto.BaseRegionAddPDTO;
import cn.jtfadmin.core.modules.base.region.domain.dto.BaseRegionEditPDTO;
import cn.jtfadmin.core.modules.base.region.domain.dto.BaseRegionModelDTO;
import cn.jtfadmin.core.modules.base.region.domain.entity.BaseRegionEntity;
import cn.jtfadmin.core.modules.base.region.enums.RegionTypeEnum;
import cn.jtfadmin.core.modules.base.region.repository.BaseRegionRepository;
import cn.jtfadmin.core.modules.base.region.service.BaseRegionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.cache.CacheManager;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.util.*;

/**
 * BaseRegionEntity 服务实现类
 */
@Service
@Validated
public class BaseRegionServiceImpl extends JpaTreeService<BaseRegionEntity> implements BaseRegionService,
        DictEntityService<BaseRegionEntity>,
        DynamicDictConfig {


    @Autowired
    private BaseRegionRepository repository;

    @Autowired
    private CacheManager cacheManager;

    private String cacheName = getClass().getSimpleName();


    public JpaRepositoryImplementation<BaseRegionEntity, String> getJpaRepository() {
        return repository;
    }


    @Override
    protected  BaseRegionEntity doAdd(Object addPDTO) {
        removeAllCache();
        return super.doAdd(addPDTO);
    }

    @Override
    protected <V extends EditPDTO> BaseRegionEntity doEdit(V editPDTO) {
        removeAllCache();
        return super.doEdit(editPDTO);
    }

    @Override
    public void deleteBatch(@NotNull Collection<String> ids) {
        removeAllCache();
        super.deleteBatch(ids);
    }

    @Override
    protected  Class getAddClass() {
        return BaseRegionAddPDTO.class;
    }

    @Override
    protected  Class getEditClass() {
        return BaseRegionEditPDTO.class;
    }

    @Transactional
    public void init() {
        removeAllCache();
        InputStream inputStream = BaseRegionServiceImpl.class.getResourceAsStream("region.json");
        repository.deleteAll(repository.findAll());
        repository.flush();
        List<AreaDTO> areaDTOS = JsonUtils.constuctListType(inputStream, AreaDTO.class);
        int i = 0;
        for (AreaDTO areaDTO : areaDTOS) {
            saveAreaDTO(areaDTO, i++, null);
        }

    }

    private void saveAreaDTO(AreaDTO areaDTO, Integer sort, String parentId) {
        BaseRegionAddPDTO dto = new BaseRegionAddPDTO();
        dto.setName(areaDTO.getName());
        dto.setNo(areaDTO.getCode());
        dto.setParentId(parentId);
        dto.setSort(sort);
        dto.setType(RegionTypeEnum.class.getEnumConstants()[areaDTO.getLevel()]);
        BaseRegionEntity entity = doAdd(dto);
        List<AreaDTO> areaList = areaDTO.getAreaList();
        if (!CollectionUtils.isEmpty(areaList)) {
            int i = 0;
            for (AreaDTO child : areaList) {
                saveAreaDTO(child, i++, entity.getId());
            }
        }

    }



    @Override
    public ResultDTO<PageRDTO<TreeRDTO<BaseRegionModelDTO>>> findPageTree(SpecPagePDTO<BaseRegionEntity> specPDTO) {
        PageRDTO<BaseRegionEntity> page = findAll(specPDTO);
        List<BaseRegionEntity> list = new ArrayList<>(page.getContent());
        List<BaseRegionEntity> sort = findAll(null, Sort.by("sort"));
        Set<String> ids = new HashSet<>();
        page.getContent().forEach(t -> ids.add(t.getId()));

        for (BaseRegionEntity baseRegionEntity : sort) {
            for (String id : ids) {
                if (StringUtils.hasText(baseRegionEntity.getParentIds())
                        && baseRegionEntity.getParentIds().contains(id) && !ids.contains(baseRegionEntity.getId())) {
                    ids.add(baseRegionEntity.getId());
                    list.add(baseRegionEntity);
                    break;
                }
            }
        }
        List<TreeRDTO<BaseRegionModelDTO>> result = TreeUtils.buildTreeRDTO(BaseRegionModelDTO.class, list);
        return ResultDTO.successData(new PageRDTO<>(result, page.getTotalElements()));
    }

    @Override
    public BaseRegionEntity findRequiredByNo(@NotBlank String no) {
        return repository.findOne((root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("no"), no)).get();
    }

    @Override
    public List<DictItemModelDTO> findChildrenDictModel(String no) {
        List<BaseRegionEntity> list ;
        if (StringUtils.hasText(no)) {
            list = findChildren(findRequiredByNo(no).getId());
        } else {
            list = findAll((root, query, criteriaBuilder) ->
                 criteriaBuilder.or(
                        criteriaBuilder.isNull(root.get("parentId")),
                        criteriaBuilder.equal(root.get("parentId"), "")
                 )
            , Sort.by("sort"));
        }
        return entityToItemModel(list, "no", "name");
    }

    @Override
    public Optional<BaseRegionEntity> findOneByNameAndParentNo(@NotBlank String name, String parentNo) {
        if (StringUtils.hasText(parentNo)) {
            String id = findRequiredByNo(parentNo).getId();
            return repository.findOne((root, query, criteriaBuilder) -> {
                return criteriaBuilder.and(
                        criteriaBuilder.equal(root.get("parentId"), id),
                        criteriaBuilder.equal(root.get("name"), name)
                );
            });
        } else {
            return repository.findOne((root, query, criteriaBuilder) -> {
                return criteriaBuilder.and(
                        criteriaBuilder.or(
                                criteriaBuilder.isNull(root.get("parentId")),
                                criteriaBuilder.equal(root.get("parentId"), "")
                        ),
                        criteriaBuilder.equal(root.get("name"), name)
                );
            });
        }

    }


    @Override
    public Set<String> getDictCode() {
        return new HashSet<>(Arrays.asList(DICT_CODE_REGION));
    }


    public void removeAllCache() {
        cacheManager.getCache(cacheName).removeAll();
    }

    @Override
    public List<DictModelDTO> getInitDict() {
        Object dict = cacheManager.getCache(cacheName)
                .get("dict");
        if (Objects.nonNull(dict)) {
            return (List<DictModelDTO>) dict;
        }
        List<DictModelDTO> sysDictModelDTOS = new ArrayList<>();
        sysDictModelDTOS.add(
                new DictModelDTO(DICT_CODE_REGION, "区域字典",
                        findDictModelList("name", "no")
                )
        );
        cacheManager.getCache(cacheName).put("dict", sysDictModelDTOS);
        return sysDictModelDTOS;
    }
}
